webengine/osswebengine/WebKit/History/WebHistoryItem.mm
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2005 Apple Computer, 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 "WebFrameBridge.h"
       
    33 #import "WebFrameInternal.h"
       
    34 #import "WebFrameView.h"
       
    35 #import "WebHTMLViewInternal.h"
       
    36 #import "WebIconDatabase.h"
       
    37 #import "WebKitLogging.h"
       
    38 #import "WebKitNSStringExtras.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 <JavaScriptCore/Assertions.h>
       
    46 #import <WebCore/CachedPage.h>
       
    47 #import <WebCore/HistoryItem.h>
       
    48 #import <WebCore/Image.h>
       
    49 #import <WebCore/KURL.h>
       
    50 #import <WebCore/PageCache.h>
       
    51 #import <WebCore/PlatformString.h>
       
    52 #import <WebCore/ThreadCheck.h>
       
    53 #import <WebCore/WebCoreObjCExtras.h>
       
    54 #import <WebKitSystemInterface.h>
       
    55 
       
    56 // Private keys used in the WebHistoryItem's dictionary representation.
       
    57 // see 3245793 for explanation of "lastVisitedDate"
       
    58 static NSString *WebLastVisitedTimeIntervalKey = @"lastVisitedDate";
       
    59 static NSString *WebVisitCountKey = @"visitCount";
       
    60 static NSString *WebTitleKey = @"title";
       
    61 static NSString *WebChildrenKey = @"children";
       
    62 static NSString *WebDisplayTitleKey = @"displayTitle";
       
    63 
       
    64 // Notification strings.
       
    65 NSString *WebHistoryItemChangedNotification = @"WebHistoryItemChangedNotification";
       
    66 
       
    67 using namespace WebCore;
       
    68 
       
    69 static inline WebHistoryItemPrivate* kitPrivate(WebCoreHistoryItem* list) { return (WebHistoryItemPrivate*)list; }
       
    70 static inline WebCoreHistoryItem* core(WebHistoryItemPrivate* list) { return (WebCoreHistoryItem*)list; }
       
    71 
       
    72 HashMap<HistoryItem*, WebHistoryItem*>& historyItemWrappers()
       
    73 {
       
    74     static HashMap<HistoryItem*, WebHistoryItem*> historyItemWrappers;
       
    75     return historyItemWrappers;
       
    76 }
       
    77 
       
    78 void WKNotifyHistoryItemChanged()
       
    79 {
       
    80     [[NSNotificationCenter defaultCenter]
       
    81         postNotificationName:WebHistoryItemChangedNotification object:nil userInfo:nil];
       
    82 }
       
    83 
       
    84 @implementation WebHistoryItem
       
    85 
       
    86 #ifndef BUILDING_ON_TIGER
       
    87 + (void)initialize
       
    88 {
       
    89     WebCoreObjCFinalizeOnMainThread(self);
       
    90 }
       
    91 #endif
       
    92 
       
    93 - (id)init
       
    94 {
       
    95     return [self initWithWebCoreHistoryItem:(new HistoryItem)];
       
    96 }
       
    97 
       
    98 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title lastVisitedTimeInterval:(NSTimeInterval)time
       
    99 {
       
   100     WebCoreThreadViolationCheck();
       
   101     return [self initWithWebCoreHistoryItem:(new HistoryItem(URLString, title, time))];
       
   102 }
       
   103 
       
   104 - (void)dealloc
       
   105 {
       
   106     WebCoreThreadViolationCheck();
       
   107     if (_private) {
       
   108         HistoryItem* coreItem = core(_private);
       
   109         coreItem->deref();
       
   110         historyItemWrappers().remove(coreItem);
       
   111     }
       
   112     [super dealloc];
       
   113 }
       
   114 
       
   115 - (void)finalize
       
   116 {
       
   117     WebCoreThreadViolationCheck();
       
   118     // FIXME: ~HistoryItem is what releases the history item's icon from the icon database
       
   119     // It's probably not good to release icons from the database only when the object is garbage-collected. 
       
   120     // Need to change design so this happens at a predictable time.
       
   121     if (_private) {
       
   122         HistoryItem* coreItem = core(_private);
       
   123         coreItem->deref();
       
   124         historyItemWrappers().remove(coreItem);
       
   125     }
       
   126     [super finalize];
       
   127 }
       
   128 
       
   129 - (id)copyWithZone:(NSZone *)zone
       
   130 {
       
   131     WebCoreThreadViolationCheck();
       
   132     WebHistoryItem *copy = (WebHistoryItem *)NSCopyObject(self, 0, zone);
       
   133     RefPtr<HistoryItem> item = core(_private)->copy();
       
   134     copy->_private = kitPrivate(item.get());
       
   135     historyItemWrappers().set(item.release().releaseRef(), copy);
       
   136     
       
   137     return copy;
       
   138 }
       
   139 
       
   140 // FIXME: Need to decide if this class ever returns URLs and decide on the name of this method
       
   141 - (NSString *)URLString
       
   142 {
       
   143     ASSERT_MAIN_THREAD();
       
   144     return nsStringNilIfEmpty(core(_private)->urlString());
       
   145 }
       
   146 
       
   147 // The first URL we loaded to get to where this history item points.  Includes both client
       
   148 // and server redirects.
       
   149 - (NSString *)originalURLString
       
   150 {
       
   151     ASSERT_MAIN_THREAD();
       
   152     return nsStringNilIfEmpty(core(_private)->originalURLString());
       
   153 }
       
   154 
       
   155 - (NSString *)title
       
   156 {
       
   157     ASSERT_MAIN_THREAD();
       
   158     return nsStringNilIfEmpty(core(_private)->title());
       
   159 }
       
   160 
       
   161 - (void)setAlternateTitle:(NSString *)alternateTitle
       
   162 {
       
   163     core(_private)->setAlternateTitle(alternateTitle);
       
   164 }
       
   165 
       
   166 - (NSString *)alternateTitle;
       
   167 {
       
   168     return nsStringNilIfEmpty(core(_private)->alternateTitle());
       
   169 }
       
   170 
       
   171 - (NSImage *)icon
       
   172 {
       
   173     return [[WebIconDatabase sharedIconDatabase] iconForURL:[self URLString] withSize:WebIconSmallSize];
       
   174     
       
   175     // FIXME: Ideally, this code should simply be the following -
       
   176     // return core(_private)->icon()->getNSImage();
       
   177     // Once radar -
       
   178     // <rdar://problem/4906567> - NSImage returned from WebCore::Image may be incorrect size
       
   179     // is resolved
       
   180 }
       
   181 
       
   182 - (NSTimeInterval)lastVisitedTimeInterval
       
   183 {
       
   184     ASSERT_MAIN_THREAD();
       
   185     return core(_private)->lastVisitedTime();
       
   186 }
       
   187 
       
   188 - (unsigned)hash
       
   189 {
       
   190     return [(NSString*)core(_private)->urlString() hash];
       
   191 }
       
   192 
       
   193 - (BOOL)isEqual:(id)anObject
       
   194 {
       
   195     ASSERT_MAIN_THREAD();
       
   196     if (![anObject isMemberOfClass:[WebHistoryItem class]]) {
       
   197         return NO;
       
   198     }
       
   199     
       
   200     return core(_private)->urlString() == core(((WebHistoryItem*)anObject)->_private)->urlString();
       
   201 }
       
   202 
       
   203 - (NSString *)description
       
   204 {
       
   205     ASSERT_MAIN_THREAD();
       
   206     HistoryItem* coreItem = core(_private);
       
   207     NSMutableString *result = [NSMutableString stringWithFormat:@"%@ %@", [super description], (NSString*)coreItem->urlString()];
       
   208     if (coreItem->target()) {
       
   209         [result appendFormat:@" in \"%@\"", (NSString*)coreItem->target()];
       
   210     }
       
   211     if (coreItem->isTargetItem()) {
       
   212         [result appendString:@" *target*"];
       
   213     }
       
   214     if (coreItem->formData()) {
       
   215         [result appendString:@" *POST*"];
       
   216     }
       
   217     
       
   218     if (coreItem->children().size()) {
       
   219         const HistoryItemVector& children = coreItem->children();
       
   220         int currPos = [result length];
       
   221         unsigned size = children.size();        
       
   222         for (unsigned i = 0; i < size; ++i) {
       
   223             WebHistoryItem *child = kit(children[i].get());
       
   224             [result appendString:@"\n"];
       
   225             [result appendString:[child description]];
       
   226         }
       
   227         // shift all the contents over.  A bit slow, but hey, this is for debugging.
       
   228         NSRange replRange = {currPos, [result length]-currPos};
       
   229         [result replaceOccurrencesOfString:@"\n" withString:@"\n    " options:0 range:replRange];
       
   230     }
       
   231     
       
   232     return result;
       
   233 }
       
   234 
       
   235 @end
       
   236 
       
   237 @interface WebWindowWatcher : NSObject
       
   238 @end
       
   239 
       
   240 
       
   241 @implementation WebHistoryItem (WebInternal)
       
   242 
       
   243 HistoryItem* core(WebHistoryItem *item)
       
   244 {
       
   245     if (!item)
       
   246         return 0;
       
   247     return core(item->_private);
       
   248 }
       
   249 
       
   250 WebHistoryItem *kit(HistoryItem* item)
       
   251 {
       
   252     if (!item)
       
   253         return nil;
       
   254         
       
   255     WebHistoryItem *kitItem = historyItemWrappers().get(item);
       
   256     if (kitItem)
       
   257         return kitItem;
       
   258     
       
   259     return [[[WebHistoryItem alloc] initWithWebCoreHistoryItem:item] autorelease];
       
   260 }
       
   261 
       
   262 + (WebHistoryItem *)entryWithURL:(NSURL *)URL
       
   263 {
       
   264     return [[[self alloc] initWithURL:URL title:nil] autorelease];
       
   265 }
       
   266 
       
   267 static WebWindowWatcher *_windowWatcher = nil;
       
   268 
       
   269 + (void)initWindowWatcherIfNecessary
       
   270 {
       
   271     if (_windowWatcher)
       
   272         return;
       
   273     _windowWatcher = [[WebWindowWatcher alloc] init];
       
   274     [[NSNotificationCenter defaultCenter] addObserver:_windowWatcher selector:@selector(windowWillClose:)
       
   275         name:NSWindowWillCloseNotification object:nil];
       
   276 }
       
   277 
       
   278 - (id)initWithURL:(NSURL *)URL target:(NSString *)target parent:(NSString *)parent title:(NSString *)title
       
   279 {
       
   280     return [self initWithWebCoreHistoryItem:(new HistoryItem(URL, target, parent, title))];
       
   281 }
       
   282 
       
   283 - (id)initWithURLString:(NSString *)URLString title:(NSString *)title displayTitle:(NSString *)displayTitle lastVisitedTimeInterval:(NSTimeInterval)time
       
   284 {
       
   285     return [self initWithWebCoreHistoryItem:(new HistoryItem(URLString, title, displayTitle, time))];
       
   286 }
       
   287 
       
   288 - (id)initWithWebCoreHistoryItem:(PassRefPtr<HistoryItem>)item
       
   289 {   
       
   290     WebCoreThreadViolationCheck();
       
   291     // Need to tell WebCore what function to call for the 
       
   292     // "History Item has Changed" notification - no harm in doing this
       
   293     // everytime a WebHistoryItem is created
       
   294     // Note: We also do this in [WebFrameView initWithFrame:] where we do
       
   295     // other "init before WebKit is used" type things
       
   296     WebCore::notifyHistoryItemChanged = WKNotifyHistoryItemChanged;
       
   297     
       
   298     self = [super init];
       
   299     
       
   300     _private = kitPrivate(item.releaseRef());
       
   301     ASSERT(!historyItemWrappers().get(core(_private)));
       
   302     historyItemWrappers().set(core(_private), self);
       
   303     return self;
       
   304 }
       
   305 
       
   306 - (void)setTitle:(NSString *)title
       
   307 {
       
   308     core(_private)->setTitle(title);
       
   309 }
       
   310 
       
   311 - (void)setVisitCount:(int)count
       
   312 {
       
   313     core(_private)->setVisitCount(count);
       
   314 }
       
   315 
       
   316 - (void)setViewState:(id)statePList;
       
   317 {
       
   318     core(_private)->setViewState(statePList);
       
   319 }
       
   320 
       
   321 - (void)_mergeAutoCompleteHints:(WebHistoryItem *)otherItem
       
   322 {
       
   323     ASSERT_ARG(otherItem, otherItem);
       
   324     core(_private)->mergeAutoCompleteHints(core(otherItem->_private));
       
   325 }
       
   326 
       
   327 - (id)initFromDictionaryRepresentation:(NSDictionary *)dict
       
   328 {
       
   329     ASSERT_MAIN_THREAD();
       
   330     NSString *URLString = [dict _webkit_stringForKey:@""];
       
   331     NSString *title = [dict _webkit_stringForKey:WebTitleKey];
       
   332 
       
   333     // Do an existence check to avoid calling doubleValue on a nil string. Leave
       
   334     // time interval at 0 if there's no value in dict.
       
   335     NSString *timeIntervalString = [dict _webkit_stringForKey:WebLastVisitedTimeIntervalKey];
       
   336     NSTimeInterval lastVisited = timeIntervalString == nil ? 0 : [timeIntervalString doubleValue];
       
   337 
       
   338     self = [self initWithURLString:URLString title:title displayTitle:[dict _webkit_stringForKey:WebDisplayTitleKey] lastVisitedTimeInterval:lastVisited];
       
   339     
       
   340     // Check if we've read a broken URL from the file that has non-Latin1 chars.  If so, try to convert
       
   341     // as if it was from user typing.
       
   342     if (![URLString canBeConvertedToEncoding:NSISOLatin1StringEncoding]) {
       
   343         NSURL *tempURL = [NSURL _web_URLWithUserTypedString:URLString];
       
   344         ASSERT(tempURL);
       
   345         NSString *newURLString = [tempURL _web_originalDataAsString];
       
   346         core(_private)->setURLString(newURLString);
       
   347         core(_private)->setOriginalURLString(newURLString);
       
   348     } 
       
   349 
       
   350     core(_private)->setVisitCount([dict _webkit_intForKey:WebVisitCountKey]);
       
   351 
       
   352     NSArray *childDicts = [dict objectForKey:WebChildrenKey];
       
   353     if (childDicts) {
       
   354         for (int i = [childDicts count]; i >= 0; i--) {
       
   355             WebHistoryItem *child = [[WebHistoryItem alloc] initFromDictionaryRepresentation: [childDicts objectAtIndex:i]];
       
   356             core(_private)->addChildItem(core(child->_private));
       
   357             [child release];
       
   358         }
       
   359     }
       
   360 
       
   361     return self;
       
   362 }
       
   363 
       
   364 - (NSPoint)scrollPoint
       
   365 {
       
   366     ASSERT_MAIN_THREAD();
       
   367     return core(_private)->scrollPoint();
       
   368 }
       
   369 
       
   370 @end
       
   371 
       
   372 @implementation WebHistoryItem (WebPrivate)
       
   373 
       
   374 - (id)initWithURL:(NSURL *)URL title:(NSString *)title
       
   375 {
       
   376     return [self initWithURLString:[URL _web_originalDataAsString] title:title lastVisitedTimeInterval:0];
       
   377 }
       
   378 
       
   379 - (NSDictionary *)dictionaryRepresentation
       
   380 {
       
   381     ASSERT_MAIN_THREAD();
       
   382     NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:6];
       
   383 
       
   384     HistoryItem* coreItem = core(_private);
       
   385     
       
   386     if (!coreItem->urlString().isEmpty()) {
       
   387         [dict setObject:(NSString*)coreItem->urlString() forKey:@""];
       
   388     }
       
   389     if (!coreItem->title().isEmpty()) {
       
   390         [dict setObject:(NSString*)coreItem->title() forKey:WebTitleKey];
       
   391     }
       
   392     if (!coreItem->alternateTitle().isEmpty()) {
       
   393         [dict setObject:(NSString*)coreItem->alternateTitle() forKey:WebDisplayTitleKey];
       
   394     }
       
   395     if (coreItem->lastVisitedTime() != 0.0) {
       
   396         // store as a string to maintain backward compatibility (see 3245793)
       
   397         [dict setObject:[NSString stringWithFormat:@"%.1lf", coreItem->lastVisitedTime()]
       
   398                  forKey:WebLastVisitedTimeIntervalKey];
       
   399     }
       
   400     if (coreItem->visitCount()) {
       
   401         [dict setObject:[NSNumber numberWithInt:coreItem->visitCount()] forKey:WebVisitCountKey];
       
   402     }
       
   403     if (coreItem->children().size()) {
       
   404         const HistoryItemVector& children = coreItem->children();
       
   405         NSMutableArray *childDicts = [NSMutableArray arrayWithCapacity:children.size()];
       
   406         
       
   407         for (int i = children.size(); i >= 0; i--)
       
   408             [childDicts addObject:[kit(children[i].get()) dictionaryRepresentation]];
       
   409         [dict setObject: childDicts forKey:WebChildrenKey];
       
   410     }
       
   411 
       
   412     return dict;
       
   413 }
       
   414 
       
   415 - (NSString *)target
       
   416 {
       
   417     ASSERT_MAIN_THREAD();
       
   418     return nsStringNilIfEmpty(core(_private)->target());
       
   419 }
       
   420 
       
   421 - (BOOL)isTargetItem
       
   422 {
       
   423     return core(_private)->isTargetItem();
       
   424 }
       
   425 
       
   426 - (int)visitCount
       
   427 {
       
   428     ASSERT_MAIN_THREAD();
       
   429     return core(_private)->visitCount();
       
   430 }
       
   431 
       
   432 - (NSString *)RSSFeedReferrer
       
   433 {
       
   434     return nsStringNilIfEmpty(core(_private)->rssFeedReferrer());
       
   435 }
       
   436 
       
   437 - (void)setRSSFeedReferrer:(NSString *)referrer
       
   438 {
       
   439     core(_private)->setRSSFeedReferrer(referrer);
       
   440 }
       
   441 
       
   442 - (NSArray *)children
       
   443 {
       
   444     ASSERT_MAIN_THREAD();
       
   445     const HistoryItemVector& children = core(_private)->children();
       
   446     if (!children.size())
       
   447         return nil;
       
   448 
       
   449     unsigned size = children.size();
       
   450     NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease];
       
   451     
       
   452     for (unsigned i = 0; i < size; ++i)
       
   453         [result addObject:kit(children[i].get())];
       
   454     
       
   455     return result;
       
   456 }
       
   457 
       
   458 - (void)setAlwaysAttemptToUsePageCache:(BOOL)flag
       
   459 {
       
   460     // Safari 2.0 uses this for SnapBack, so we stub it out to avoid a crash.
       
   461 }
       
   462 
       
   463 - (NSURL *)URL
       
   464 {
       
   465     ASSERT_MAIN_THREAD();
       
   466     KURL url = core(_private)->url();
       
   467     return url.isEmpty() ? nil : url.getNSURL();
       
   468 }
       
   469 
       
   470 // This should not be called directly for WebHistoryItems that are already included
       
   471 // in WebHistory. Use -[WebHistory setLastVisitedTimeInterval:forItem:] instead.
       
   472 - (void)_setLastVisitedTimeInterval:(NSTimeInterval)time
       
   473 {
       
   474     core(_private)->setLastVisitedTime(time);
       
   475 }
       
   476 
       
   477 // FIXME: <rdar://problem/4880065> - Push Global History into WebCore
       
   478 // Once that task is complete, this accessor can go away
       
   479 - (NSCalendarDate *)_lastVisitedDate
       
   480 {
       
   481     ASSERT_MAIN_THREAD();
       
   482     return [[[NSCalendarDate alloc] initWithTimeIntervalSinceReferenceDate:core(_private)->lastVisitedTime()] autorelease];
       
   483 }
       
   484 
       
   485 - (WebHistoryItem *)targetItem
       
   486 {    
       
   487     ASSERT_MAIN_THREAD();
       
   488     HistoryItem* coreItem = core(_private);
       
   489     if (coreItem->isTargetItem() || !coreItem->hasChildren())
       
   490         return self;
       
   491     return kit(coreItem->recurseToFindTargetItem());
       
   492 }
       
   493 
       
   494 + (void)_releaseAllPendingPageCaches
       
   495 {
       
   496     pageCache()->releaseAutoreleasedPagesNow();
       
   497 }
       
   498 
       
   499 - (id)_transientPropertyForKey:(NSString *)key
       
   500 {
       
   501     return core(_private)->getTransientProperty(key);
       
   502 }
       
   503 
       
   504 - (void)_setTransientProperty:(id)property forKey:(NSString *)key
       
   505 {
       
   506     core(_private)->setTransientProperty(key, property);
       
   507 }
       
   508 
       
   509 @end
       
   510 
       
   511 
       
   512 // FIXME: <rdar://problem/4886761>
       
   513 // This is a bizarre policy - we flush the page caches ANY time ANY window is closed?  
       
   514 @implementation WebWindowWatcher
       
   515 -(void)windowWillClose:(NSNotification *)notification
       
   516 {
       
   517     pageCache()->releaseAutoreleasedPagesNow();
       
   518 }
       
   519 @end