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 40 using namespace WebCore; 41 42 namespace WebKit { 43 44 DEFINE_STATIC_GETTER(CFStringRef, SessionHistoryKey, (CFSTR("SessionHistory"))); 45 DEFINE_STATIC_GETTER(CFStringRef, ProvisionalURLKey, (CFSTR("ProvisionalURL"))); 46 47 static const UInt32 CurrentSessionStateDataVersion = 2; 48 49 PassRefPtr<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.adoptCF(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 107 void 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 SandboxExtension::Handle sandboxExtensionHandle; 168 if (WebBackForwardListItem* item = m_backForwardList->currentItem()) { 169 initializeSandboxExtensionHandle(KURL(KURL(), item->url()), sandboxExtensionHandle); 170 setPendingAPIRequestURL(item->url()); 171 } 172 173 process()->send(Messages::WebPage::RestoreSessionAndNavigateToCurrentItem(state, sandboxExtensionHandle), m_pageID); 174 } 175 } 176 } 177 } 178 179 if (provisionalURL) 180 loadURL(provisionalURL); 181 } 182 183 static RetainPtr<CFStringRef> autosaveKey(const String& name) 184 { 185 String key = "com.apple.WebKit.searchField:" + name; 186 return RetainPtr<CFStringRef>(AdoptCF, key.createCFString()); 187 } 188 189 void WebPageProxy::saveRecentSearches(const String& name, const Vector<String>& searchItems) 190 { 191 // The WebProcess shouldn't have bothered to send this message if the name was empty. 192 ASSERT(!name.isEmpty()); 193 194 RetainPtr<CFMutableArrayRef> items; 195 196 if (size_t size = searchItems.size()) { 197 items.adoptCF(CFArrayCreateMutable(0, size, &kCFTypeArrayCallBacks)); 198 for (size_t i = 0; i < size; ++i) { 199 RetainPtr<CFStringRef> item(AdoptCF, searchItems[i].createCFString()); 200 CFArrayAppendValue(items.get(), item.get()); 201 } 202 } 203 204 CFPreferencesSetAppValue(autosaveKey(name).get(), items.get(), kCFPreferencesCurrentApplication); 205 CFPreferencesAppSynchronize(kCFPreferencesCurrentApplication); 206 } 207 208 void WebPageProxy::loadRecentSearches(const String& name, Vector<String>& searchItems) 209 { 210 // The WebProcess shouldn't have bothered to send this message if the name was empty. 211 ASSERT(!name.isEmpty()); 212 213 searchItems.clear(); 214 RetainPtr<CFArrayRef> items(AdoptCF, reinterpret_cast<CFArrayRef>(CFPreferencesCopyAppValue(autosaveKey(name).get(), kCFPreferencesCurrentApplication))); 215 216 if (!items || CFGetTypeID(items.get()) != CFArrayGetTypeID()) 217 return; 218 219 size_t size = CFArrayGetCount(items.get()); 220 for (size_t i = 0; i < size; ++i) { 221 CFStringRef item = (CFStringRef)CFArrayGetValueAtIndex(items.get(), i); 222 if (CFGetTypeID(item) == CFStringGetTypeID()) 223 searchItems.append(item); 224 } 225 } 226 227 } // namespace WebKit 228