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