2011-05-24 Jay Civelli <jcivelli@chromium.org>
[WebKit-https.git] / Source / WebCore / loader / archive / cf / LegacyWebArchive.cpp
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 "FrameSelection.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 "SharedBuffer.h"
49 #include <wtf/text/CString.h>
50 #include <wtf/text/WTFString.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     return create(KURL(), data);
266 }
267
268 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const KURL&, SharedBuffer* data)
269 {
270     LOG(Archives, "LegacyWebArchive - Creating from raw data");
271     
272     RefPtr<LegacyWebArchive> archive = create();
273         
274     ASSERT(data);
275     if (!data)
276         return 0;
277         
278     RetainPtr<CFDataRef> cfData(AdoptCF, data->createCFData());
279     if (!cfData)
280         return 0;
281         
282     CFStringRef errorString = 0;
283     
284     RetainPtr<CFDictionaryRef> plist(AdoptCF, static_cast<CFDictionaryRef>(CFPropertyListCreateFromXMLData(0, cfData.get(), kCFPropertyListImmutable, &errorString)));
285     if (!plist) {
286 #ifndef NDEBUG
287         const char* cError = errorString ? CFStringGetCStringPtr(errorString, kCFStringEncodingUTF8) : "unknown error";
288         LOG(Archives, "LegacyWebArchive - Error parsing PropertyList from archive data - %s", cError);
289 #endif
290         if (errorString)
291             CFRelease(errorString);
292         return 0;
293     }
294     
295     if (CFGetTypeID(plist.get()) != CFDictionaryGetTypeID()) {
296         LOG(Archives, "LegacyWebArchive - Archive property list is not the expected CFDictionary, aborting invalid WebArchive");
297         return 0;
298     }
299     
300     if (!archive->extract(plist.get()))
301         return 0;
302
303     return archive.release();
304 }
305
306 bool LegacyWebArchive::extract(CFDictionaryRef dictionary)
307 {
308     ASSERT(dictionary);
309     if (!dictionary) {
310         LOG(Archives, "LegacyWebArchive - Null root CFDictionary, aborting invalid WebArchive");
311         return false;
312     }
313     
314     CFDictionaryRef mainResourceDict = static_cast<CFDictionaryRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveMainResourceKey));
315     if (!mainResourceDict) {
316         LOG(Archives, "LegacyWebArchive - No main resource in archive, aborting invalid WebArchive");
317         return false;
318     }
319     if (CFGetTypeID(mainResourceDict) != CFDictionaryGetTypeID()) {
320         LOG(Archives, "LegacyWebArchive - Main resource is not the expected CFDictionary, aborting invalid WebArchive");
321         return false;
322     }
323     
324     setMainResource(createResource(mainResourceDict));
325     if (!mainResource()) {
326         LOG(Archives, "LegacyWebArchive - Failed to parse main resource from CFDictionary or main resource does not exist, aborting invalid WebArchive");
327         return false;
328     }
329     
330     CFArrayRef subresourceArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubresourcesKey));
331     if (subresourceArray && CFGetTypeID(subresourceArray) != CFArrayGetTypeID()) {
332         LOG(Archives, "LegacyWebArchive - Subresources is not the expected Array, aborting invalid WebArchive");
333         return false;
334     }
335     
336     if (subresourceArray) {
337         CFIndex count = CFArrayGetCount(subresourceArray);
338         for (CFIndex i = 0; i < count; ++i) {
339             CFDictionaryRef subresourceDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subresourceArray, i));
340             if (CFGetTypeID(subresourceDict) != CFDictionaryGetTypeID()) {
341                 LOG(Archives, "LegacyWebArchive - Subresource is not expected CFDictionary, aborting invalid WebArchive");
342                 return false;
343             }
344             addSubresource(createResource(subresourceDict));
345         }
346     }
347     
348     CFArrayRef subframeArray = static_cast<CFArrayRef>(CFDictionaryGetValue(dictionary, LegacyWebArchiveSubframeArchivesKey));
349     if (subframeArray && CFGetTypeID(subframeArray) != CFArrayGetTypeID()) {
350         LOG(Archives, "LegacyWebArchive - Subframe archives is not the expected Array, aborting invalid WebArchive");
351         return false;
352     }
353     
354     if (subframeArray) {
355         CFIndex count = CFArrayGetCount(subframeArray);
356         for (CFIndex i = 0; i < count; ++i) {
357             CFDictionaryRef subframeDict = static_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(subframeArray, i));
358             if (CFGetTypeID(subframeDict) != CFDictionaryGetTypeID()) {
359                 LOG(Archives, "LegacyWebArchive - Subframe array is not expected CFDictionary, aborting invalid WebArchive");
360                 return false;
361             }
362             
363             RefPtr<LegacyWebArchive> subframeArchive = create();
364             if (subframeArchive->extract(subframeDict))
365                 addSubframeArchive(subframeArchive.release());
366             else
367                 LOG(Archives, "LegacyWebArchive - Invalid subframe archive skipped");
368         }
369     }
370     
371     return true;
372 }
373
374 Archive::Type LegacyWebArchive::type() const
375 {
376     return Archive::WebArchive;
377 }
378     
379 RetainPtr<CFDataRef> LegacyWebArchive::rawDataRepresentation()
380 {
381     RetainPtr<CFDictionaryRef> propertyList = createPropertyListRepresentation(this);
382     ASSERT(propertyList);
383     if (!propertyList) {
384         LOG(Archives, "LegacyWebArchive - Failed to create property list for archive, returning no data");
385         return 0;
386     }
387
388     RetainPtr<CFWriteStreamRef> stream(AdoptCF, CFWriteStreamCreateWithAllocatedBuffers(0, 0));
389
390     CFWriteStreamOpen(stream.get());
391     CFPropertyListWriteToStream(propertyList.get(), stream.get(), kCFPropertyListBinaryFormat_v1_0, 0);
392
393     RetainPtr<CFDataRef> plistData(AdoptCF, static_cast<CFDataRef>(CFWriteStreamCopyProperty(stream.get(), kCFStreamPropertyDataWritten)));
394     ASSERT(plistData);
395
396     CFWriteStreamClose(stream.get());
397
398     if (!plistData) {
399         LOG(Archives, "LegacyWebArchive - Failed to convert property list into raw data, returning no data");
400         return 0;
401     }
402
403     return plistData;
404 }
405
406 #if !PLATFORM(MAC)
407
408 ResourceResponse LegacyWebArchive::createResourceResponseFromMacArchivedData(CFDataRef responseData)
409 {
410     // FIXME: If is is possible to parse in a serialized NSURLResponse manually, without using
411     // NSKeyedUnarchiver, manipulating plists directly, then we want to do that here.
412     // Until then, this can be done on Mac only.
413     return ResourceResponse();
414 }
415
416 RetainPtr<CFDataRef> LegacyWebArchive::createPropertyListRepresentation(const ResourceResponse& response)
417 {
418     // FIXME: Write out the "new" format described in createResourceResponseFromPropertyListData once we invent it.
419     return 0;
420 }
421
422 #endif
423
424 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Node* node)
425 {
426     ASSERT(node);
427     if (!node)
428         return create();
429         
430     Document* document = node->document();
431     Frame* frame = document ? document->frame() : 0;
432     if (!frame)
433         return create();
434         
435     Vector<Node*> nodeList;
436     String markupString = createMarkup(node, IncludeNode, &nodeList);
437     Node::NodeType nodeType = node->nodeType();
438     if (nodeType != Node::DOCUMENT_NODE && nodeType != Node::DOCUMENT_TYPE_NODE)
439         markupString = frame->documentTypeString() + markupString;
440
441     return create(markupString, frame, nodeList);
442 }
443
444 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Frame* frame)
445 {
446     ASSERT(frame);
447     
448     DocumentLoader* documentLoader = frame->loader()->documentLoader();
449
450     if (!documentLoader)
451         return 0;
452         
453     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
454     
455     unsigned children = frame->tree()->childCount();
456     for (unsigned i = 0; i < children; ++i) {
457         RefPtr<LegacyWebArchive> childFrameArchive = create(frame->tree()->child(i));
458         if (childFrameArchive)
459             subframeArchives.append(childFrameArchive.release());
460     }
461
462     Vector<PassRefPtr<ArchiveResource> > subresources;
463     documentLoader->getSubresources(subresources);
464
465     return create(documentLoader->mainResource(), subresources, subframeArchives);
466 }
467
468 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(Range* range)
469 {
470     if (!range)
471         return 0;
472     
473     Node* startContainer = range->startContainer();
474     if (!startContainer)
475         return 0;
476         
477     Document* document = startContainer->document();
478     if (!document)
479         return 0;
480         
481     Frame* frame = document->frame();
482     if (!frame)
483         return 0;
484     
485     Vector<Node*> nodeList;
486     
487     // FIXME: This is always "for interchange". Is that right? See the previous method.
488     String markupString = frame->documentTypeString() + createMarkup(range, &nodeList, AnnotateForInterchange);
489
490     return create(markupString, frame, nodeList);
491 }
492
493 PassRefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame* frame, const Vector<Node*>& nodes)
494 {
495     ASSERT(frame);
496     
497     const ResourceResponse& response = frame->loader()->documentLoader()->response();
498     KURL responseURL = response.url();
499     
500     // it's possible to have a response without a URL here
501     // <rdar://problem/5454935>
502     if (responseURL.isNull())
503         responseURL = KURL(ParsedURLString, "");
504         
505     PassRefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame->tree()->uniqueName());
506
507     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
508     Vector<PassRefPtr<ArchiveResource> > subresources;
509     HashSet<KURL> uniqueSubresources;
510
511     size_t nodesSize = nodes.size();    
512     for (size_t i = 0; i < nodesSize; ++i) {
513         Node* node = nodes[i];
514         Frame* childFrame;
515         if ((node->hasTagName(HTMLNames::frameTag) || node->hasTagName(HTMLNames::iframeTag) || node->hasTagName(HTMLNames::objectTag)) &&
516              (childFrame = static_cast<HTMLFrameOwnerElement*>(node)->contentFrame())) {
517             RefPtr<LegacyWebArchive> subframeArchive = create(childFrame->document());
518             
519             if (subframeArchive)
520                 subframeArchives.append(subframeArchive);
521             else
522                 LOG_ERROR("Unabled to archive subframe %s", childFrame->tree()->uniqueName().string().utf8().data());
523         } else {
524             ListHashSet<KURL> subresourceURLs;
525             node->getSubresourceURLs(subresourceURLs);
526             
527             DocumentLoader* documentLoader = frame->loader()->documentLoader();
528             ListHashSet<KURL>::iterator iterEnd = subresourceURLs.end();
529             for (ListHashSet<KURL>::iterator iter = subresourceURLs.begin(); iter != iterEnd; ++iter) {
530                 const KURL& subresourceURL = *iter;
531                 if (uniqueSubresources.contains(subresourceURL))
532                     continue;
533
534                 uniqueSubresources.add(subresourceURL);
535
536                 RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL);
537                 if (resource) {
538                     subresources.append(resource.release());
539                     continue;
540                 }
541
542                 CachedResource* cachedResource = memoryCache()->resourceForURL(subresourceURL);
543                 if (cachedResource) {
544                     resource = ArchiveResource::create(cachedResource->data(), subresourceURL, cachedResource->response());
545                     if (resource) {
546                         subresources.append(resource.release());
547                         continue;
548                     }
549                 }
550
551                 // FIXME: should do something better than spew to console here
552                 LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data());
553             }
554         }
555     }
556
557     // Add favicon if one exists for this page, if we are archiving the entire page.
558     if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase().isEnabled()) {
559         const String& iconURL = iconDatabase().synchronousIconURLForPageURL(responseURL);
560         if (!iconURL.isEmpty() && iconDatabase().synchronousIconDataKnownForIconURL(iconURL)) {
561             if (Image* iconImage = iconDatabase().synchronousIconForPageURL(responseURL, IntSize(16, 16))) {
562                 if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), KURL(ParsedURLString, iconURL), "image/x-icon", "", ""))
563                     subresources.append(resource.release());
564             }
565         }
566     }
567
568     return create(mainResource, subresources, subframeArchives);
569 }
570
571 PassRefPtr<LegacyWebArchive> LegacyWebArchive::createFromSelection(Frame* frame)
572 {
573     if (!frame)
574         return 0;
575     
576     RefPtr<Range> selectionRange = frame->selection()->toNormalizedRange();
577     Vector<Node*> nodeList;
578     String markupString = frame->documentTypeString() + createMarkup(selectionRange.get(), &nodeList, AnnotateForInterchange);
579     
580     RefPtr<LegacyWebArchive> archive = create(markupString, frame, nodeList);
581     
582     if (!frame->document() || !frame->document()->isFrameSet())
583         return archive.release();
584         
585     // Wrap the frameset document in an iframe so it can be pasted into
586     // another document (which will have a body or frameset of its own). 
587     String iframeMarkup = "<iframe frameborder=\"no\" marginwidth=\"0\" marginheight=\"0\" width=\"98%%\" height=\"98%%\" src=\"" +
588                           frame->loader()->documentLoader()->response().url().string() + "\"></iframe>";
589     RefPtr<ArchiveResource> iframeResource = ArchiveResource::create(utf8Buffer(iframeMarkup), blankURL(), "text/html", "UTF-8", String());
590
591     Vector<PassRefPtr<ArchiveResource> > subresources;
592
593     Vector<PassRefPtr<LegacyWebArchive> > subframeArchives;
594     subframeArchives.append(archive);
595     
596     archive = create(iframeResource.release(), subresources, subframeArchives);
597     
598     return archive.release();
599 }
600
601 }