Home | History | Annotate | Download | only in cf
      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