Home | History | Annotate | Download | only in cf
      1 /*
      2  * Copyright (C) 2008, 2009 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  *
      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 #include "config.h"
     30 #include "LegacyWebArchive.h"
     31 
     32 #include "MemoryCache.h"
     33 #include "Document.h"
     34 #include "DocumentLoader.h"
     35 #include "Frame.h"
     36 #include "FrameLoader.h"
     37 #include "FrameTree.h"
     38 #include "HTMLFrameOwnerElement.h"
     39 #include "HTMLNames.h"
     40 #include "IconDatabase.h"
     41 #include "Image.h"
     42 #include "KURLHash.h"
     43 #include "Logging.h"
     44 #include "markup.h"
     45 #include "Node.h"
     46 #include "Range.h"
     47 #include "SelectionController.h"
     48 #include "SharedBuffer.h"
     49 #include <wtf/text/CString.h>
     50 #include <wtf/text/StringConcatenate.h>
     51 #include <wtf/ListHashSet.h>
     52 #include <wtf/RetainPtr.h>
     53 
     54 namespace WebCore {
     55 
     56 static const CFStringRef LegacyWebArchiveMainResourceKey = CFSTR("WebMainResource");
     57 static const CFStringRef LegacyWebArchiveSubresourcesKey = CFSTR("WebSubresources");
     58 static const CFStringRef LegacyWebArchiveSubframeArchivesKey = CFSTR("WebSubframeArchives");
     59 static const CFStringRef LegacyWebArchiveResourceDataKey = CFSTR("WebResourceData");
     60 static const CFStringRef LegacyWebArchiveResourceFrameNameKey = CFSTR("WebResourceFrameName");
     61 static const CFStringRef LegacyWebArchiveResourceMIMETypeKey = CFSTR("WebResourceMIMEType");
     62 static const CFStringRef LegacyWebArchiveResourceURLKey = CFSTR("WebResourceURL");
     63 static const CFStringRef LegacyWebArchiveResourceTextEncodingNameKey = CFSTR("WebResourceTextEncodingName");
     64 static const CFStringRef LegacyWebArchiveResourceResponseKey = CFSTR("WebResourceResponse");
     65 static const CFStringRef LegacyWebArchiveResourceResponseVersionKey = CFSTR("WebResourceResponseVersion");
     66 
     67 RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(ArchiveResource* resource, MainResourceStatus isMainResource)
     68 {
     69     if (!resource) {
     70         // The property list representation of a null/empty WebResource has the following 3 objects stored as nil.
     71         // FIXME: 0 is not serializable. Presumably we need to use kCFNull here instead for compatibility.
     72         // FIXME: But why do we need to support a resource of 0? Who relies on that?
     73         RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, 0));
     74         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, 0);
     75         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, 0);
     76         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, 0);
     77         return propertyList;
     78     }
     79 
     80     RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 6, 0, &kCFTypeDictionaryValueCallBacks));
     81 
     82     // Resource data can be empty, but must be represented by an empty CFDataRef
     83     SharedBuffer* data = resource->data();
     84     RetainPtr<CFDataRef> cfData;
     85     if (data)
     86         cfData.adoptCF(data->createCFData());
     87     else
     88         cfData.adoptCF(CFDataCreate(0, 0, 0));
     89     CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceDataKey, cfData.get());
     90 
     91     // Resource URL cannot be null
     92     RetainPtr<CFStringRef> cfURL(AdoptCF, resource->url().string().createCFString());
     93     if (cfURL)
     94         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceURLKey, cfURL.get());
     95     else {
     96         LOG(Archives, "LegacyWebArchive - NULL resource URL is invalid - returning null property list");
     97         return 0;
     98     }
     99 
    100     // FrameName should be left out if empty for subresources, but always included for main resources
    101     const String& frameName(resource->frameName());
    102     if (!frameName.isEmpty() || isMainResource) {
    103         RetainPtr<CFStringRef> cfFrameName(AdoptCF, frameName.createCFString());
    104         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceFrameNameKey, cfFrameName.get());
    105     }
    106 
    107     // Set MIMEType, TextEncodingName, and ResourceResponse only if they actually exist
    108     const String& mimeType(resource->mimeType());
    109     if (!mimeType.isEmpty()) {
    110         RetainPtr<CFStringRef> cfMIMEType(AdoptCF, mimeType.createCFString());
    111         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceMIMETypeKey, cfMIMEType.get());
    112     }
    113 
    114     const String& textEncoding(resource->textEncoding());
    115     if (!textEncoding.isEmpty()) {
    116         RetainPtr<CFStringRef> cfTextEncoding(AdoptCF, textEncoding.createCFString());
    117         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceTextEncodingNameKey, cfTextEncoding.get());
    118     }
    119 
    120     // Don't include the resource response for the main resource
    121     if (!isMainResource) {
    122         RetainPtr<CFDataRef> resourceResponseData = createPropertyListRepresentation(resource->response());
    123         if (resourceResponseData)
    124             CFDictionarySetValue(propertyList.get(), LegacyWebArchiveResourceResponseKey, resourceResponseData.get());
    125     }
    126 
    127     return propertyList;
    128 }
    129 
    130 RetainPtr<CFDictionaryRef> LegacyWebArchive::createPropertyListRepresentation(Archive* archive)
    131 {
    132     RetainPtr<CFMutableDictionaryRef> propertyList(AdoptCF, CFDictionaryCreateMutable(0, 3, 0, &kCFTypeDictionaryValueCallBacks));
    133 
    134     RetainPtr<CFDictionaryRef> mainResourceDict = createPropertyListRepresentation(archive->mainResource(), MainResource);
    135     ASSERT(mainResourceDict);
    136     if (!mainResourceDict)
    137         return 0;
    138     CFDictionarySetValue(propertyList.get(), LegacyWebArchiveMainResourceKey, mainResourceDict.get());
    139 
    140     RetainPtr<CFMutableArrayRef> subresourcesArray(AdoptCF, CFArrayCreateMutable(0, archive->subresources().size(), &kCFTypeArrayCallBacks));
    141     const Vector<RefPtr<ArchiveResource> >& subresources(archive->subresources());
    142     for (unsigned i = 0; i < subresources.size(); ++i) {
    143         RetainPtr<CFDictionaryRef> subresource = createPropertyListRepresentation(subresources[i].get(), Subresource);
    144         if (subresource)
    145             CFArrayAppendValue(subresourcesArray.get(), subresource.get());
    146         else
    147             LOG(Archives, "LegacyWebArchive - Failed to create property list for subresource");
    148     }
    149     if (CFArrayGetCount(subresourcesArray.get()))
    150         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubresourcesKey, subresourcesArray.get());
    151 
    152     RetainPtr<CFMutableArrayRef> subframesArray(AdoptCF, CFArrayCreateMutable(0, archive->subframeArchives().size(), &kCFTypeArrayCallBacks));
    153     const Vector<RefPtr<Archive> >& subframeArchives(archive->subframeArchives());
    154     for (unsigned i = 0; i < subframeArchives.size(); ++i) {
    155         RetainPtr<CFDictionaryRef> subframeArchive = createPropertyListRepresentation(subframeArchives[i].get());
    156         if (subframeArchive)
    157             CFArrayAppendValue(subframesArray.get(), subframeArchive.get());
    158         else
    159             LOG(Archives, "LegacyWebArchive - Failed to create property list for subframe archive");
    160     }
    161     if (CFArrayGetCount(subframesArray.get()))
    162         CFDictionarySetValue(propertyList.get(), LegacyWebArchiveSubframeArchivesKey, subframesArray.get());
    163 
    164     return propertyList;
    165 }
    166 
    167 ResourceResponse LegacyWebArchive::createResourceResponseFromPropertyListData(CFDataRef data, CFStringRef responseDataType)
    168 {
    169     ASSERT(data);
    170     if (!data)
    171         return ResourceResponse();
    172 
    173     // If the ResourceResponseVersion (passed in as responseDataType) exists at all, this is a "new" web archive that we
    174     // can parse well in a cross platform manner If it doesn't exist, we will assume this is an "old" web archive with,
    175     // NSURLResponse objects in it and parse the ResourceResponse as such.
    176     if (!responseDataType)
    177         return createResourceResponseFromMacArchivedData(data);
    178 
    179     // FIXME: Parse the "new" format that the above comment references here. This format doesn't exist yet.
    180     return ResourceResponse();
    181 }
    182 
    183 PassRefPtr<ArchiveResource> LegacyWebArchive::createResource(CFDictionaryRef dictionary)
    184 {
    185     ASSERT(dictionary);
    186     if (!dictionary)
    187         return 0;
    188 
    189     CFDataRef resourceData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceDataKey));
    190     if (resourceData && CFGetTypeID(resourceData) != CFDataGetTypeID()) {
    191         LOG(Archives, "LegacyWebArchive - Resource data is not of type CFData, cannot create invalid resource");
    192         return 0;
    193     }
    194 
    195     CFStringRef frameName = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceFrameNameKey));
    196     if (frameName && CFGetTypeID(frameName) != CFStringGetTypeID()) {
    197         LOG(Archives, "LegacyWebArchive - Frame name is not of type CFString, cannot create invalid resource");
    198         return 0;
    199     }
    200 
    201     CFStringRef mimeType = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceMIMETypeKey));
    202     if (!mimeType || CFGetTypeID(mimeType) != CFStringGetTypeID()) {
    203         LOG(Archives, "LegacyWebArchive - MIME type is not of type CFString, cannot create invalid resource");
    204         return 0;
    205     }
    206 
    207     CFStringRef url = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceURLKey));
    208     if (url && CFGetTypeID(url) != CFStringGetTypeID()) {
    209         LOG(Archives, "LegacyWebArchive - URL is not of type CFString, cannot create invalid resource");
    210         return 0;
    211     }
    212 
    213     CFStringRef textEncoding = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceTextEncodingNameKey));
    214     if (textEncoding && CFGetTypeID(textEncoding) != CFStringGetTypeID()) {
    215         LOG(Archives, "LegacyWebArchive - Text encoding is not of type CFString, cannot create invalid resource");
    216         return 0;
    217     }
    218 
    219     ResourceResponse response;
    220 
    221     CFDataRef resourceResponseData = static_cast<CFDataRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseKey));
    222     if (resourceResponseData) {
    223         if (CFGetTypeID(resourceResponseData) != CFDataGetTypeID()) {
    224             LOG(Archives, "LegacyWebArchive - Resource response data is not of type CFData, cannot create invalid resource");
    225             return 0;
    226         }
    227 
    228         CFStringRef resourceResponseVersion = static_cast<CFStringRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveResourceResponseVersionKey));
    229         if (resourceResponseVersion && CFGetTypeID(resourceResponseVersion) != CFStringGetTypeID()) {
    230             LOG(Archives, "LegacyWebArchive - Resource response version is not of type CFString, cannot create invalid resource");
    231             return 0;
    232         }
    233 
    234         response = createResourceResponseFromPropertyListData(resourceResponseData, resourceResponseVersion);
    235     }
    236 
    237     return ArchiveResource::create(SharedBuffer::wrapCFData(resourceData), KURL(KURL(), url), mimeType, textEncoding, frameName, response);
    238 }
    239 
    240 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create()
    241 {
    242     return adoptRef(new LegacyWebArchive);
    243 }
    244 
    245 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(PassRefPtr<ArchiveResource> mainResource, Vector<PassRefPtr<ArchiveResource> >& subresources, Vector<PassRefPtr<LegacyWebArchive> >& subframeArchives)
    246 {
    247     ASSERT(mainResource);
    248     if (!mainResource)
    249         return 0;
    250 
    251     RefPtr<LegacyWebArchive> archive = create();
    252     archive->setMainResource(mainResource);
    253 
    254     for (unsigned i = 0; i < subresources.size(); ++i)
    255         archive->addSubresource(subresources[i]);
    256 
    257     for (unsigned i = 0; i < subframeArchives.size(); ++i)
    258         archive->addSubframeArchive(subframeArchives[i]);
    259 
    260     return archive.release();
    261 }
    262 
    263 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(SharedBuffer* data)
    264 {
    265     LOG(Archives, "LegacyWebArchive - Creating from raw data");
    266 
    267     RefPtr<LegacyWebArchive> archive = create();
    268 
    269     ASSERT(data);
    270     if (!data)
    271         return 0;
    272 
    273     RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
    274     if (!cfData)
    275         return 0;
    276 
    277     CFStringRef errorString = 0;
    278 
    279     RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString)));
    280     if (!plist) {
    281 #ifndef NDEBUG
    282         const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error";
    283         LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
    284 #endif
    285         if (errorString)
    286             CFRelease(errorString);
    287         return 0;
    288     }
    289 
    290     if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
    291         LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
    292         return 0;
    293     }
    294 
    295     if (!archive->extract(plist.get()))
    296         return 0;
    297 
    298     return archive.release();
    299 }
    300 
    301 bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
    302 {
    303     ASSERT(dictionary);
    304     if (!dictionary) {
    305         LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
    306         return false;
    307     }
    308 
    309     CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
    310     if (!mainResourceDict) {
    311         LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
    312         return false;
    313     }
    314     if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
    315         LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
    316         return false;
    317     }
    318 
    319     setMainResource(createResource(mainResourceDict));
    320     if (!mainResource()) {
    321         LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
    322         return false;
    323     }
    324 
    325     CFArrayRef subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey));
    326     if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) {
    327         LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive");
    328         return false;
    329     }
    330 
    331     if (subresourceArray) {
    332         CFIndex count = CFArrayGetCount(subresourceArray);
    333         for (CFIndex i = 0; i < count; ++i) {
    334             CFDictionaryRef subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i));
    335             if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) {
    336                 LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive");
    337                 return false;
    338             }
    339             addSubresource(createResource(subresourceDict));
    340         }
    341     }
    342 
    343     CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
    344     if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
    345         LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
    346         return false;
    347     }
    348 
    349     if (subframeArray) {
    350         CFIndex count = CFArrayGetCount(subframeArray);
    351         for (CFIndex i = 0; i < count; ++i) {
    352             CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
    353             if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
    354                 LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
    355                 return false;
    356             }
    357 
    358             RefPtr<LegacyWebArchive> subframeArchive = create();
    359             if (subframeArchive->extract(subframeDict))
    360                 addSubframeArchive(subframeArchive.release());
    361             else
    362                 LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
    363         }
    364     }
    365 
    366     return true;
    367 }
    368 
    369 RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
    370 {
    371     RetainPtr<CFDictionaryRef> propertyList = createPropertyListRepresentation(this);
    372     ASSERT(propertyList);
    373     if (!propertyList) {
    374         LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
    375         return 0;
    376     }
    377 
    378     RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithAllocatedBuffers(0, 0));
    379 
    380     CFWriteStreamOpen(stream.get());
    381     CFPropertyListWriteToStream(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0);
    382 
    383     RetainPtr<CFDataRef> plistData(AdoptCF, static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten)));
    384     ASSERT(plistData);
    385 
    386     CFWriteStreamClose(stream.get());
    387 
    388     if (!plistData) {
    389         LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
    390         return 0;
    391     }
    392 
    393     return plistData;
    394 }
    395 
    396 #if !PLATFORM(MAC)
    397 
    398 ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData)
    399 {
    400     // FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using
    401     // NSKeyedUnarchiver, manipulating plists directly, then we want to do that here.
    402     // Until then, this can be done on Mac only.
    403     return ResourceResponse();
    404 }
    405 
    406 RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response)
    407 {
    408     // FIXME: Write out the "new" format described in createResourceResponseFromPropertyListData once we invent it.
    409     return 0;
    410 }
    411 
    412 #endif
    413 
    414 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node)
    415 {
    416     ASSERT(node);
    417     if (!node)
    418         return create();
    419 
    420     Document* document = node->document();
    421     Frame* frame = document ? document->frame() : 0;
    422     if (!frame)
    423         return create();
    424 
    425     Vector<Node*> nodeList;
    426     String markupString = createMarkup(node, IncludeNode, &nodeList);
    427     Node::NodeType nodeType = node->nodeType();
    428     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
    429         markupString = frame->documentTypeString() + markupString;
    430 
    431     return create(markupString, frame, nodeList);
    432 }
    433 
    434 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame)
    435 {
    436     ASSERT(frame);
    437 
    438     DocumentLoader* documentLoader = frame->loader()->documentLoader();
    439 
    440     if (!documentLoader)
    441         return 0;
    442 
    443     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
    444 
    445     unsigned children = frame->tree()->childCount();
    446     for (unsigned i = 0; i < children; ++i) {
    447         RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i));
    448         if (childFrameArchive)
    449             subframeArchives.append(childFrameArchive.release());
    450     }
    451 
    452     Vector<PassRefPtr<ArchiveResource> > subresources;
    453     documentLoader->getSubresources(subresources);
    454 
    455     return create(documentLoader->mainResource(), subresources, subframeArchives);
    456 }
    457 
    458 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
    459 {
    460     if (!range)
    461         return 0;
    462 
    463     Node* startContainer = range->startContainer();
    464     if (!startContainer)
    465         return 0;
    466 
    467     Document* document = startContainer->document();
    468     if (!document)
    469         return 0;
    470 
    471     Frame* frame = document->frame();
    472     if (!frame)
    473         return 0;
    474 
    475     Vector<Node*> nodeList;
    476 
    477     // FIXME: This is always "for interchange". Is that right? See the previous method.
    478     String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange);
    479 
    480     return create(markupString, frame, nodeList);
    481 }
    482 
    483 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, const Vector<Node*>& nodes)
    484 {
    485     ASSERT(frame);
    486 
    487     const ResourceResponse& response = frame->loader()->documentLoader()->response();
    488     KURL responseURL = response.url();
    489 
    490     // it's possible to have a response without a URL here
    491     // <rdar://problem/5454935>
    492     if (responseURL.isNull())
    493         responseURL = KURL(ParsedURLString, "");
    494 
    495     PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->uniqueName());
    496 
    497     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
    498     Vector<PassRefPtr<ArchiveResource> > subresources;
    499     HashSet<KURL> uniqueSubresources;
    500 
    501     size_t nodesSize = nodes.size();
    502     for (size_t i = 0; i < nodesSize; ++i) {
    503         Node* node = nodes[i];
    504         Frame* childFrame;
    505         if ((node->hasTagName(HTMLNames::frameTag) || node->hasTagName(HTMLNames::iframeTag) || node->hasTagName(HTMLNames::objectTag)) &&
    506              (childFrame = static_cast<HTMLFrameOwnerElement*>(node)->contentFrame())) {
    507             RefPtr<LegacyWebArchive> subframeArchive = create(childFrame->document());
    508 
    509             if (subframeArchive)
    510                 subframeArchives.append(subframeArchive);
    511             else
    512                 LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->uniqueName().string().utf8().data());
    513         } else {
    514             ListHashSet<KURL> subresourceURLs;
    515             node->getSubresourceURLs(subresourceURLs);
    516 
    517             DocumentLoader* documentLoader = frame->loader()->documentLoader();
    518             ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end();
    519             for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) {
    520                 const KURL& subresourceURL = *iter;
    521                 if (uniqueSubresources.contains(subresourceURL))
    522                     continue;
    523 
    524                 uniqueSubresources.add(subresourceURL);
    525 
    526                 RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL);
    527                 if (resource) {
    528                     subresources.append(resource.release());
    529                     continue;
    530                 }
    531 
    532                 CachedResource* cachedResource = memoryCache()->resourceForURL(subresourceURL);
    533                 if (cachedResource) {
    534                     resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response());
    535                     if (resource) {
    536                         subresources.append(resource.release());
    537                         continue;
    538                     }
    539                 }
    540 
    541                 // FIXME: should do something better than spew to console here
    542                 LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data());
    543             }
    544         }
    545     }
    546 
    547     // Add favicon if one exists for this page, if we are archiving the entire page.
    548     if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase().isEnabled()) {
    549         const String& iconURL = iconDatabase().synchronousIconURLForPageURL(responseURL);
    550         if (!iconURL.isEmpty() && iconDatabase().synchronousIconDataKnownForIconURL(iconURL)) {
    551             if (Image* iconImage = iconDatabase().synchronousIconForPageURL(responseURL, IntSize(16, 16))) {
    552                 if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(ParsedURLString, iconURL), "image/x-icon", "", ""))
    553                     subresources.append(resource.release());
    554             }
    555         }
    556     }
    557 
    558     return create(mainResource, subresources, subframeArchives);
    559 }
    560 
    561 PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
    562 {
    563     if (!frame)
    564         return 0;
    565 
    566     RefPtr<Range> selectionRange = frame->selection()->toNormalizedRange();
    567     Vector<Node*> nodeList;
    568     String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange);
    569 
    570     RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList);
    571 
    572     if (!frame->document() || !frame->document()->isFrameSet())
    573         return archive.release();
    574 
    575     // Wrap the frameset document in an iframe so it can be pasted into
    576     // another document (which will have a body or frameset of its own).
    577     String iframeMarkup = makeString("<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"",
    578                                      frame->loader()->documentLoader()->response().url().string(), "\"></iframe>");
    579     RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String());
    580 
    581     Vector<PassRefPtr<ArchiveResource> > subresources;
    582 
    583     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
    584     subframeArchives.append(archive);
    585 
    586     archive = create(iframeResource.release(), subresources, subframeArchives);
    587 
    588     return archive.release();
    589 }
    590 
    591 }
    592