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