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