1/* 2 * Copyright (C) 2010, 2011 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 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "WebPageProxy.h" 28 29#include "DataReference.h" 30#include "Logging.h" 31#include "SessionState.h" 32#include "WebBackForwardList.h" 33#include "WebData.h" 34#include "WebPageMessages.h" 35#include "WebProcessProxy.h" 36 37#include <wtf/RetainPtr.h> 38#include <CoreFoundation/CFPropertyList.h> 39 40using namespace WebCore; 41 42namespace WebKit { 43 44DEFINE_STATIC_GETTER(CFStringRef, SessionHistoryKey, (CFSTR("SessionHistory"))); 45DEFINE_STATIC_GETTER(CFStringRef, ProvisionalURLKey, (CFSTR("ProvisionalURL"))); 46 47static const UInt32 CurrentSessionStateDataVersion = 2; 48 49PassRefPtr<WebData> WebPageProxy::sessionStateData(WebPageProxySessionStateFilterCallback filter, void* context) const 50{ 51 const void* keys[2]; 52 const void* values[2]; 53 CFIndex numValues = 0; 54 55 RetainPtr<CFDictionaryRef> sessionHistoryDictionary = adoptCF(m_backForwardList->createCFDictionaryRepresentation(filter, context)); 56 if (sessionHistoryDictionary) { 57 keys[numValues] = SessionHistoryKey(); 58 values[numValues] = sessionHistoryDictionary.get(); 59 ++numValues; 60 } 61 62 RetainPtr<CFStringRef> provisionalURLString; 63 if (m_mainFrame) { 64 String provisionalURL = pendingAPIRequestURL(); 65 if (provisionalURL.isEmpty()) 66 provisionalURL = m_mainFrame->provisionalURL(); 67 if (!provisionalURL.isEmpty()) { 68 provisionalURLString = provisionalURL.createCFString(); 69 keys[numValues] = ProvisionalURLKey(); 70 values[numValues] = provisionalURLString.get(); 71 ++numValues; 72 } 73 } 74 75 if (!numValues) 76 return 0; 77 78 RetainPtr<CFDictionaryRef> stateDictionary = adoptCF(CFDictionaryCreate(0, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)); 79 80 RetainPtr<CFWriteStreamRef> writeStream = adoptCF(CFWriteStreamCreateWithAllocatedBuffers(0, 0)); 81 if (!writeStream) 82 return 0; 83 84 if (!CFWriteStreamOpen(writeStream.get())) 85 return 0; 86 87 if (!CFPropertyListWriteToStream(stateDictionary.get(), writeStream.get(), kCFPropertyListBinaryFormat_v1_0, 0)) 88 return 0; 89 90 RetainPtr<CFDataRef> stateCFData = adoptCF((CFDataRef)CFWriteStreamCopyProperty(writeStream.get(), kCFStreamPropertyDataWritten)); 91 92 CFIndex length = CFDataGetLength(stateCFData.get()); 93 Vector<unsigned char> stateVector(length + sizeof(UInt32)); 94 95 // Put the session state version number at the start of the buffer 96 stateVector.data()[0] = (CurrentSessionStateDataVersion & 0xFF000000) >> 24; 97 stateVector.data()[1] = (CurrentSessionStateDataVersion & 0x00FF0000) >> 16; 98 stateVector.data()[2] = (CurrentSessionStateDataVersion & 0x0000FF00) >> 8; 99 stateVector.data()[3] = (CurrentSessionStateDataVersion & 0x000000FF); 100 101 // Copy in the actual session state data 102 CFDataGetBytes(stateCFData.get(), CFRangeMake(0, length), stateVector.data() + sizeof(UInt32)); 103 104 return WebData::create(stateVector); 105} 106 107void WebPageProxy::restoreFromSessionStateData(WebData* webData) 108{ 109 if (!webData || webData->size() < sizeof(UInt32)) 110 return; 111 112 const unsigned char* buffer = webData->bytes(); 113 UInt32 versionHeader = (buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]; 114 115 if (versionHeader != CurrentSessionStateDataVersion) { 116 LOG(SessionState, "Unrecognized version header for session state data - cannot restore"); 117 return; 118 } 119 120 RetainPtr<CFDataRef> data = adoptCF(CFDataCreate(0, webData->bytes() + sizeof(UInt32), webData->size() - sizeof(UInt32))); 121 122 CFStringRef propertyListError = 0; 123 RetainPtr<CFPropertyListRef> propertyList = adoptCF(CFPropertyListCreateFromXMLData(0, data.get(), kCFPropertyListImmutable, &propertyListError)); 124 if (propertyListError) { 125 CFRelease(propertyListError); 126 LOG(SessionState, "Could not read session state property list"); 127 return; 128 } 129 130 if (!propertyList) 131 return; 132 133 if (CFGetTypeID(propertyList.get()) != CFDictionaryGetTypeID()) { 134 LOG(SessionState, "SessionState property list is not a CFDictionaryRef (%i) - its CFTypeID is %i", (int)CFDictionaryGetTypeID(), (int)CFGetTypeID(propertyList.get())); 135 return; 136 } 137 138 CFDictionaryRef backForwardListDictionary = 0; 139 if (CFTypeRef value = CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList.get()), SessionHistoryKey())) { 140 if (CFGetTypeID(value) != CFDictionaryGetTypeID()) 141 LOG(SessionState, "SessionState dictionary has a SessionHistory key, but the value is not a dictionary"); 142 else 143 backForwardListDictionary = static_cast<CFDictionaryRef>(value); 144 } 145 146 CFStringRef provisionalURL = 0; 147 if (CFTypeRef value = CFDictionaryGetValue(static_cast<CFDictionaryRef>(propertyList.get()), ProvisionalURLKey())) { 148 if (CFGetTypeID(value) != CFStringGetTypeID()) 149 LOG(SessionState, "SessionState dictionary has a ProvisionalValue key, but the value is not a string"); 150 else 151 provisionalURL = static_cast<CFStringRef>(value); 152 } 153 154 if (backForwardListDictionary) { 155 if (!m_backForwardList->restoreFromCFDictionaryRepresentation(backForwardListDictionary)) 156 LOG(SessionState, "Failed to restore back/forward list from SessionHistory dictionary"); 157 else { 158 const BackForwardListItemVector& entries = m_backForwardList->entries(); 159 if (size_t size = entries.size()) { 160 for (size_t i = 0; i < size; ++i) 161 process()->registerNewWebBackForwardListItem(entries[i].get()); 162 163 SessionState state(m_backForwardList->entries(), m_backForwardList->currentIndex()); 164 if (provisionalURL) 165 process()->send(Messages::WebPage::RestoreSession(state), m_pageID); 166 else { 167 if (WebBackForwardListItem* item = m_backForwardList->currentItem()) 168 setPendingAPIRequestURL(item->url()); 169 170 process()->send(Messages::WebPage::RestoreSessionAndNavigateToCurrentItem(state), m_pageID); 171 } 172 } 173 } 174 } 175 176 if (provisionalURL) 177 loadURL(provisionalURL); 178} 179 180static RetainPtr<CFStringRef> autosaveKey(const String& name) 181{ 182 return String("com.apple.WebKit.searchField:" + name).createCFString(); 183} 184 185void WebPageProxy::saveRecentSearches(const String& name, const Vector<String>& searchItems) 186{ 187 // The WebProcess shouldn't have bothered to send this message if the name was empty. 188 ASSERT(!name.isEmpty()); 189 190 RetainPtr<CFMutableArrayRef> items; 191 192 if (size_t size = searchItems.size()) { 193 items = adoptCF(CFArrayCreateMutable(0, size, &kCFTypeArrayCallBacks)); 194 for (size_t i = 0; i < size; ++i) 195 CFArrayAppendValue(items.get(), searchItems[i].createCFString().get()); 196 } 197 198 CFPreferencesSetAppValue(autosaveKey(name).get(), items.get(), kCFPreferencesCurrentApplication); 199 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 200} 201 202void WebPageProxy::loadRecentSearches(const String& name, Vector<String>& searchItems) 203{ 204 // The WebProcess shouldn't have bothered to send this message if the name was empty. 205 ASSERT(!name.isEmpty()); 206 207 searchItems.clear(); 208 RetainPtr<CFArrayRef> items = adoptCF(reinterpret_cast<CFArrayRef>(CFPreferencesCopyAppValue(autosaveKey(name).get(), kCFPreferencesCurrentApplication))); 209 210 if (!items || CFGetTypeID(items.get()) != CFArrayGetTypeID()) 211 return; 212 213 size_t size = CFArrayGetCount(items.get()); 214 for (size_t i = 0; i < size; ++i) { 215 CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(items.get(), i); 216 if (CFGetTypeID(item) == CFStringGetTypeID()) 217 searchItems.append(item); 218 } 219} 220 221} // namespace WebKit 222