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