| /* |
| * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "WebBackForwardList.h" |
| #import "WebBackForwardListInternal.h" |
| |
| #import "BackForwardList.h" |
| #import "WebFrameInternal.h" |
| #import "WebHistoryItemInternal.h" |
| #import "WebHistoryItemPrivate.h" |
| #import "WebKitLogging.h" |
| #import "WebKitVersionChecks.h" |
| #import "WebNSObjectExtras.h" |
| #import "WebPreferencesPrivate.h" |
| #import "WebTypesInternal.h" |
| #import "WebViewPrivate.h" |
| #import <JavaScriptCore/InitializeThreading.h> |
| #import <WebCore/HistoryItem.h> |
| #import <WebCore/PageCache.h> |
| #import <WebCore/Settings.h> |
| #import <WebCore/ThreadCheck.h> |
| #import <WebCore/WebCoreObjCExtras.h> |
| #import <wtf/Assertions.h> |
| #import <wtf/MainThread.h> |
| #import <wtf/NeverDestroyed.h> |
| #import <wtf/RunLoop.h> |
| #import <wtf/StdLibExtras.h> |
| |
| typedef HashMap<BackForwardList*, WebBackForwardList*> BackForwardListMap; |
| |
| // FIXME: Instead of this we could just create a class derived from BackForwardList |
| // with a pointer to a WebBackForwardList in it. |
| static BackForwardListMap& backForwardLists() |
| { |
| static NeverDestroyed<BackForwardListMap> staticBackForwardLists; |
| return staticBackForwardLists; |
| } |
| |
| @implementation WebBackForwardList |
| |
| BackForwardList* core(WebBackForwardList *webBackForwardList) |
| { |
| if (!webBackForwardList) |
| return 0; |
| |
| return reinterpret_cast<BackForwardList*>(webBackForwardList->_private); |
| } |
| |
| WebBackForwardList *kit(BackForwardList* backForwardList) |
| { |
| if (!backForwardList) |
| return nil; |
| |
| if (WebBackForwardList *webBackForwardList = backForwardLists().get(backForwardList)) |
| return webBackForwardList; |
| |
| return [[[WebBackForwardList alloc] initWithBackForwardList:*backForwardList] autorelease]; |
| } |
| |
| - (id)initWithBackForwardList:(Ref<BackForwardList>&&)backForwardList |
| { |
| WebCoreThreadViolationCheckRoundOne(); |
| self = [super init]; |
| if (!self) |
| return nil; |
| |
| _private = reinterpret_cast<WebBackForwardListPrivate*>(&backForwardList.leakRef()); |
| backForwardLists().set(core(self), self); |
| return self; |
| } |
| |
| + (void)initialize |
| { |
| #if !PLATFORM(IOS_FAMILY) |
| JSC::initializeThreading(); |
| WTF::initializeMainThreadToProcessMainThread(); |
| RunLoop::initializeMainRunLoop(); |
| #endif |
| } |
| |
| - (id)init |
| { |
| return [self initWithBackForwardList:BackForwardList::create(nullptr)]; |
| } |
| |
| - (void)dealloc |
| { |
| if (WebCoreObjCScheduleDeallocateOnMainThread([WebBackForwardList class], self)) |
| return; |
| |
| BackForwardList* backForwardList = core(self); |
| ASSERT(backForwardList); |
| if (backForwardList) { |
| ASSERT(backForwardList->closed()); |
| backForwardLists().remove(backForwardList); |
| backForwardList->deref(); |
| } |
| |
| [super dealloc]; |
| } |
| |
| - (void)_close |
| { |
| core(self)->close(); |
| } |
| |
| - (void)addItem:(WebHistoryItem *)entry |
| { |
| ASSERT(entry); |
| core(self)->addItem(*core(entry)); |
| |
| // Since the assumed contract with WebBackForwardList is that it retains its WebHistoryItems, |
| // the following line prevents a whole class of problems where a history item will be created in |
| // a function, added to the BFlist, then used in the rest of that function. |
| [[entry retain] autorelease]; |
| } |
| |
| - (void)removeItem:(WebHistoryItem *)item |
| { |
| if (!item) |
| return; |
| |
| core(self)->removeItem(*core(item)); |
| } |
| |
| #if PLATFORM(IOS_FAMILY) |
| |
| // FIXME: Move into WebCore the code that deals directly with WebCore::BackForwardList. |
| |
| #define WebBackForwardListDictionaryEntriesKey @"entries" |
| #define WebBackForwardListDictionaryCapacityKey @"capacity" |
| #define WebBackForwardListDictionaryCurrentKey @"current" |
| |
| - (NSDictionary *)dictionaryRepresentation |
| { |
| BackForwardList *coreBFList = core(self); |
| |
| auto& historyItems = coreBFList->entries(); |
| unsigned size = historyItems.size(); |
| NSMutableArray *entriesArray = [[NSMutableArray alloc] initWithCapacity:size]; |
| for (unsigned i = 0; i < size; ++i) |
| [entriesArray addObject:[kit(const_cast<WebCore::HistoryItem*>(historyItems[i].ptr())) dictionaryRepresentationIncludingChildren:NO]]; |
| |
| NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys: |
| entriesArray, WebBackForwardListDictionaryEntriesKey, |
| [NSNumber numberWithUnsignedInt:coreBFList->current()], WebBackForwardListDictionaryCurrentKey, |
| [NSNumber numberWithInt:coreBFList->capacity()], WebBackForwardListDictionaryCapacityKey, |
| nil]; |
| |
| [entriesArray release]; |
| |
| return dictionary; |
| } |
| |
| - (void)setToMatchDictionaryRepresentation:(NSDictionary *)dictionary |
| { |
| BackForwardList *coreBFList = core(self); |
| |
| coreBFList->setCapacity([[dictionary objectForKey:WebBackForwardListDictionaryCapacityKey] intValue]); |
| |
| for (NSDictionary *itemDictionary in [dictionary objectForKey:WebBackForwardListDictionaryEntriesKey]) { |
| WebHistoryItem *item = [[WebHistoryItem alloc] initFromDictionaryRepresentation:itemDictionary]; |
| coreBFList->addItem(*core(item)); |
| [item release]; |
| } |
| |
| unsigned currentIndex = [[dictionary objectForKey:WebBackForwardListDictionaryCurrentKey] unsignedIntValue]; |
| size_t listSize = coreBFList->entries().size(); |
| if (currentIndex >= listSize) |
| currentIndex = listSize - 1; |
| coreBFList->setCurrent(currentIndex); |
| } |
| #endif // PLATFORM(IOS_FAMILY) |
| |
| - (BOOL)containsItem:(WebHistoryItem *)item |
| { |
| if (!item) |
| return NO; |
| |
| return core(self)->containsItem(*core(item)); |
| } |
| |
| - (void)goBack |
| { |
| core(self)->goBack(); |
| } |
| |
| - (void)goForward |
| { |
| core(self)->goForward(); |
| } |
| |
| - (void)goToItem:(WebHistoryItem *)item |
| { |
| if (item) |
| core(self)->goToItem(*core(item)); |
| } |
| |
| - (WebHistoryItem *)backItem |
| { |
| return [[kit(core(self)->backItem().get()) retain] autorelease]; |
| } |
| |
| - (WebHistoryItem *)currentItem |
| { |
| return [[kit(core(self)->currentItem().get()) retain] autorelease]; |
| } |
| |
| - (WebHistoryItem *)forwardItem |
| { |
| return [[kit(core(self)->forwardItem().get()) retain] autorelease]; |
| } |
| |
| static NSArray* vectorToNSArray(Vector<Ref<WebCore::HistoryItem>>& list) |
| { |
| unsigned size = list.size(); |
| NSMutableArray *result = [[[NSMutableArray alloc] initWithCapacity:size] autorelease]; |
| for (unsigned i = 0; i < size; ++i) |
| [result addObject:kit(list[i].ptr())]; |
| |
| return result; |
| } |
| |
| static bool bumperCarBackForwardHackNeeded() |
| { |
| #if !PLATFORM(IOS_FAMILY) |
| static bool hackNeeded = [[[NSBundle mainBundle] bundleIdentifier] isEqualToString:@"com.freeverse.bumpercar"] && |
| !WebKitLinkedOnOrAfter(WEBKIT_FIRST_VERSION_WITHOUT_BUMPERCAR_BACK_FORWARD_QUIRK); |
| |
| return hackNeeded; |
| #else |
| return false; |
| #endif |
| } |
| |
| - (NSArray *)backListWithLimit:(int)limit |
| { |
| Vector<Ref<WebCore::HistoryItem>> list; |
| core(self)->backListWithLimit(limit, list); |
| NSArray *result = vectorToNSArray(list); |
| |
| if (bumperCarBackForwardHackNeeded()) { |
| static NSArray *lastBackListArray = nil; |
| [lastBackListArray release]; |
| lastBackListArray = [result retain]; |
| } |
| |
| return result; |
| } |
| |
| - (NSArray *)forwardListWithLimit:(int)limit |
| { |
| Vector<Ref<WebCore::HistoryItem>> list; |
| core(self)->forwardListWithLimit(limit, list); |
| NSArray *result = vectorToNSArray(list); |
| |
| if (bumperCarBackForwardHackNeeded()) { |
| static NSArray *lastForwardListArray = nil; |
| [lastForwardListArray release]; |
| lastForwardListArray = [result retain]; |
| } |
| |
| return result; |
| } |
| |
| - (int)capacity |
| { |
| return core(self)->capacity(); |
| } |
| |
| - (void)setCapacity:(int)size |
| { |
| core(self)->setCapacity(size); |
| } |
| |
| |
| -(NSString *)description |
| { |
| NSMutableString *result; |
| |
| result = [NSMutableString stringWithCapacity:512]; |
| |
| [result appendString:@"\n--------------------------------------------\n"]; |
| [result appendString:@"WebBackForwardList:\n"]; |
| |
| BackForwardList* backForwardList = core(self); |
| auto& entries = backForwardList->entries(); |
| |
| for (unsigned i = 0; i < entries.size(); ++i) { |
| if (entries[i].ptr() == backForwardList->currentItem()) { |
| [result appendString:@" >>>"]; |
| } else { |
| [result appendString:@" "]; |
| } |
| [result appendFormat:@"%2d) ", i]; |
| int currPos = [result length]; |
| [result appendString:[kit(const_cast<WebCore::HistoryItem*>(entries[i].ptr())) description]]; |
| |
| // shift all the contents over. a bit slow, but this is for debugging |
| NSRange replRange = { static_cast<NSUInteger>(currPos), [result length] - currPos }; |
| [result replaceOccurrencesOfString:@"\n" withString:@"\n " options:0 range:replRange]; |
| |
| [result appendString:@"\n"]; |
| } |
| |
| [result appendString:@"\n--------------------------------------------\n"]; |
| |
| return result; |
| } |
| |
| - (void)setPageCacheSize:(NSUInteger)size |
| { |
| [core(self)->webView() setUsesPageCache:size != 0]; |
| } |
| |
| - (NSUInteger)pageCacheSize |
| { |
| return [core(self)->webView() usesPageCache] ? WebCore::PageCache::singleton().maxSize() : 0; |
| } |
| |
| - (int)backListCount |
| { |
| return core(self)->backListCount(); |
| } |
| |
| - (int)forwardListCount |
| { |
| return core(self)->forwardListCount(); |
| } |
| |
| - (WebHistoryItem *)itemAtIndex:(int)index |
| { |
| return [[kit(core(self)->itemAtIndex(index).get()) retain] autorelease]; |
| } |
| |
| @end |